package com.flyiu.data.idempotent.handler;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;

import com.flyiu.data.idempotent.annotation.Idempotent;
import com.flyiu.data.idempotent.model.DefaultIdempotentEntity;
import com.flyiu.foundational.util.BeanUtils;
import com.google.gson.Gson;

/**
 * @author 作者 E-mail: flyiu
 * @version 创建时间：2017年10月12日 下午2:35:01 类说明 幂等实现器
 */
public abstract class IdempotentHandler<T> {

    /** 对象HASH值key */
    public static final String           HASH_CODE                   = "hashCode";

    /** json转换 */
    private static Gson                  json                        = new Gson();

    /** 幂等对象,父类不写入，留给子类保存信息 */
    protected ThreadLocal<T>             idempotentEntityThreadLocal = new ThreadLocal<>();

    /**
     * 幂等值,根据Key获取到的值，getIdempotentKey方法写入此属性,value与value之间通过空格分隔 如 value1 value2
     */
    protected static ThreadLocal<String> idemValuesThreadLocal       = new ThreadLocal<>();

    public String getIdempotentKey() {
        return idemValuesThreadLocal.get();
    }
    
    /**
     * 获取对象属性
     * 
     * @param param
     * @param idemKeys
     * @return
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NoSuchMethodException
     */
    private static String getIdemValue(Object param,String[] idemKeys) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        StringBuffer idemValues = new StringBuffer();
        for (String idemKey : idemKeys) {
            Object idemValue = PropertyUtils.getProperty(param, idemKey);
            idemValues.append(idemValue).append(" ");
        }
        
        return idemValues.toString();
    }

    /**
     * 使用get set方法获取属性唯一值,idempotentKey+paramIndex确认获取的幂等key属性
     * 
     * @param params
     * @param idempotent
     * @param signature
     * @return DefaultIdempotentEntity
     * @throws NoSuchMethodException
     * @throws SecurityException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     * @throws JsonProcessingException
     */
    static DefaultIdempotentEntity getIdempotentKey(Object[] params, Idempotent idempotent, Signature signature) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Object param = params[idempotent.paramIndex()];
        String[] idemKeys = idempotent.idempotentKey();
        String[] businessIdkeys = idempotent.businessIdKey().length==0 ?idemKeys: idempotent.businessIdKey();

        DefaultIdempotentEntity idempotentEntity = new DefaultIdempotentEntity();
        
        String idemValue = getIdemValue(param, idemKeys);
        String businessValue = getIdemValue(param, businessIdkeys);

        idempotentEntity.setContext(BeanUtils.toString(params));
        idempotentEntity.setKey(idemValue);
        idempotentEntity.setHash(signature.getName() + json.toJson(param).hashCode());
        idempotentEntity.setBussinessId(businessValue);
        MethodSignature methodSignature = (MethodSignature)signature;
        idempotentEntity.setName(StringUtils.isEmpty(idempotent.name()) ? methodSignature.getDeclaringType().getName()+":" + signature.getName() : idempotent.name());
        idemValuesThreadLocal.set(idemValue);
        return idempotentEntity;

    }

    /**
     * 构建幂等，此方法需子类实现<br>
     * 首先进行 幂等 写入尝试，如果写入成功则返回null以表示初次请求。写尝试失败则转为查询
     * 
     * @param idempotentEntity
     *            幂等对象
     *            @param reponseType 返回类型
     * @return 返回上一次请求返回对象，null代表初次接收请求
     */
    protected abstract Object buildDuplicateResponse(DefaultIdempotentEntity idempotentEntity, Type reponseType);

    /**
     * 插入请求日志
     * 
     * @param args
     *            方法入参
     * @return
     */
    protected Object before(Object[] args, String hashCode) {
        return null;
    }

    /**
     * 请求成功之后处理，通常可更新请求状态
     * 
     * @param beforeObj
     *            执行before返回对象
     * @param responseObj
     *            执行process返回对象
     */
    protected void success(Object beforeObj, Object responseObj) {

    }

    /**
     * 请求失败之后的处理，通常可更新请求状态
     * 
     * @param beforeObj
     *            beforeObj 执行before返回对象
     * @param e
     */
    protected void failure(Object beforeObj, Exception e, ProceedingJoinPoint joinPoint) {

    }

    /**
     * 返回结果之后，执行于事务关闭后,子类继承时需注意调用super.afrerResponse,或自行remove ThreadLocal对象
     * 
     * @param responseObj
     */
    protected void afterResponse(Object responseObj) {
        idempotentEntityThreadLocal.remove();
        idemValuesThreadLocal.remove();
    }

    /**
     * 幂等征用尝试
     * 
     * @param idempotentEntity
     * @return true:征用成功，false:征用失败
     */
    public abstract boolean tryAcquire(DefaultIdempotentEntity idempotentEntity);

}
